/*
 * flash_driver.c
 * STM32F446 FLASH驱动程序源文件
 * 采用直接操作寄存器和LL库编写
 * 使用方法:
 * 1. 初始化FLASH
 * 2. 擦除扇区
 * 3. 写入数据（确保在单扇区内）
 * 4. 读取数据
 * 5. 扇区信息查询
 * 6. 检查单扇区操作
 * 注意：在实际应用中，应避免频繁擦除和写入FLASH以延长其使用寿命
 */

#include "flash_driver.h"
#include <string.h>
#include "stm32f4xx.h" // 包含CMSIS头文件，以获取SystemCoreClock定义

// 超时时间定义（根据系统时钟调整，这里假设180MHz，约0.5ms超时）
#define FLASH_TIMEOUT_VALUE    100000U

/**
 * @brief 根据地址获取扇区号
 * @param address FLASH地址
 * @return 扇区号，如果地址无效返回FLASH_SECTOR_7 + 1（超出有效范围）
 */
uint32_t FLASH_GetSectorByAddress(uint32_t address)
{
    uint32_t sector = FLASH_SECTOR_7 + 1; // 超出有效扇区范围的值作为错误返回
    
    if ((address >= 0x08000000) && (address < 0x08010000))
    {
        // 扇区0-3 (每个16KB)
        sector = (address - 0x08000000) / FLASH_SECTOR_0_1_2_3_SIZE;
    }
    else if ((address >= 0x08010000) && (address < 0x08020000))
    {
        // 扇区4 (64KB)
        sector = FLASH_SECTOR_4;
    }
    else if ((address >= 0x08020000) && (address < 0x08040000))
    {
        // 扇区5 (128KB)
        sector = FLASH_SECTOR_5;
    }
    else if ((address >= 0x08040000) && (address < 0x08080000))
    {
        // 扇区6 (256KB)
        sector = FLASH_SECTOR_6;
    }
    else if ((address >= 0x08080000) && (address < 0x080C0000))
    {
        // 扇区7 (256KB)
        sector = FLASH_SECTOR_7;
    }
    
    return sector;
}

/**
 * @brief 获取扇区大小
 * @param sector 扇区号
 * @return 扇区大小（字节）
 */
uint32_t FLASH_GetSectorSize(uint32_t sector)
{
    uint32_t size = 0;
    
    switch (sector)
    {
        case FLASH_SECTOR_0:
        case FLASH_SECTOR_1:
        case FLASH_SECTOR_2:
        case FLASH_SECTOR_3:
            size = FLASH_SECTOR_0_1_2_3_SIZE;
            break;
        case FLASH_SECTOR_4:
            size = FLASH_SECTOR_4_SIZE;
            break;
        case FLASH_SECTOR_5:
            size = FLASH_SECTOR_5_SIZE;
            break;
        case FLASH_SECTOR_6:
        case FLASH_SECTOR_7:
            size = FLASH_SECTOR_6_7_SIZE;
            break;
        default:
            size = 0;
            break;
    }
    
    return size;
}

/**
 * @brief 获取扇区起始地址
 * @param sector 扇区号
 * @return 扇区起始地址
 */
uint32_t FLASH_GetSectorStartAddress(uint32_t sector)
{
    uint32_t address = 0;
    
    switch (sector)
    {
        case FLASH_SECTOR_0:
            address = 0x08000000;
            break;
        case FLASH_SECTOR_1:
            address = 0x08004000;
            break;
        case FLASH_SECTOR_2:
            address = 0x08008000;
            break;
        case FLASH_SECTOR_3:
            address = 0x0800C000;
            break;
        case FLASH_SECTOR_4:
            address = 0x08010000;
            break;
        case FLASH_SECTOR_5:
            address = 0x08020000;
            break;
        case FLASH_SECTOR_6:
            address = 0x08040000;
            break;
        case FLASH_SECTOR_7:
            address = 0x08080000;
            break;
        default:
            address = 0;
            break;
    }
    
    return address;
}

/**
 * @brief 检查地址是否在有效范围内
 * @param address 起始地址
 * @param length 数据长度
 * @return FLASH_OK或FLASH_ADDR_ERROR
 */
uint8_t FLASH_CheckAddressValid(uint32_t address, uint32_t length)
{
    // 检查地址是否在FLASH范围内，且不超出FLASH末尾
    if ((address < FLASH_BASE_ADDR) || 
        (address + length > (FLASH_BASE_ADDR + 0x00100000)) || // 假设1MB FLASH
        (length == 0))
    {
        return FLASH_ADDR_ERROR;
    }
    
    return FLASH_OK;
}

/**
 * @brief 写入ADS1230数据到FLASH扇区5
 * @param zero_data ADS1230零位数据（int32_t类型）
 * @param calib_factor ADS1230校准因子（float类型）
 * @return FLASH_OK、FLASH_ERROR或FLASH_TIMEOUT
 * @note 数据从扇区5的起始地址开始写入
 */
uint8_t FLASH_WriteADS1230Data(int32_t zero_data, float calib_factor)
{
    uint8_t status = FLASH_OK;
    uint32_t sector5_start = FLASH_GetSectorStartAddress(FLASH_SECTOR_5);
    uint8_t buffer[8]; // 足够存储int32_t和float
    
    // 将数据转换为字节数组
    memcpy(buffer, &zero_data, sizeof(int32_t));
    memcpy(buffer + sizeof(int32_t), &calib_factor, sizeof(float));
    
    // 检查扇区5是否存在（扇区起始地址是否有效）
    if (sector5_start == 0)
    {
        return FLASH_ERROR;
    }
    
    // 检查写入长度是否在单个扇区内（8字节肯定在扇区5内）
    if (FLASH_CheckSingleSector(sector5_start, 8) != FLASH_OK)
    {
        return FLASH_LENGTH_ERROR;
    }
    
    // 擦除扇区5
    if (FLASH_EraseSector(FLASH_SECTOR_5) != FLASH_OK)
    {
        return FLASH_ERROR;
    }
    
    // 写入数据
    status = FLASH_Write(sector5_start, buffer, 8);
    
    return status;
}

/**
 * @brief 从FLASH扇区5读取ADS1230数据
 * @param zero_data 用于存储零位数据的指针
 * @param calib_factor 用于存储校准因子的指针
 * @return FLASH_OK或FLASH_ERROR
 */
uint8_t FLASH_ReadADS1230Data(int32_t *zero_data, float *calib_factor)
{
    uint8_t buffer[8];
    uint32_t sector5_start = FLASH_GetSectorStartAddress(FLASH_SECTOR_5);
    
    // 检查指针是否有效
    if ((zero_data == NULL) || (calib_factor == NULL))
    {
        return FLASH_ERROR;
    }
    
    // 检查扇区5是否存在
    if (sector5_start == 0)
    {
        return FLASH_ERROR;
    }
    
    // 读取数据
    if (FLASH_Read(sector5_start, buffer, 8) != FLASH_OK)
    {
        return FLASH_ERROR;
    }
    
    // 将字节数组转换回原始数据类型
    memcpy(zero_data, buffer, sizeof(int32_t));
    memcpy(calib_factor, buffer + sizeof(int32_t), sizeof(float));
    
    return FLASH_OK;
}

/**
 * @brief 检查是否在单个扇区内
 * @param address 起始地址
 * @param length 数据长度
 * @return FLASH_OK或FLASH_LENGTH_ERROR
 */
uint8_t FLASH_CheckSingleSector(uint32_t address, uint32_t length)
{
    uint32_t start_sector, end_sector;
    
    // 检查地址有效性
    if (FLASH_CheckAddressValid(address, length) != FLASH_OK)
    {
        return FLASH_ADDR_ERROR;
    }
    
    // 获取起始地址和结束地址所在的扇区
    start_sector = FLASH_GetSectorByAddress(address);
    end_sector = FLASH_GetSectorByAddress(address + length - 1);
    
    // 检查是否在同一个扇区内
    if (start_sector != end_sector)
    {
        return FLASH_LENGTH_ERROR;
    }
    
    return FLASH_OK;
}

/**
 * @brief 根据系统时钟频率计算FLASH等待周期
 * @return 计算得到的FLASH等待周期值
 */
static uint32_t FLASH_CalculateLatency(void)
{
    uint32_t hclk_freq = SystemCoreClock; // 获取系统时钟频率
    uint32_t latency = 0;
    
    if (hclk_freq <= 30000000U)
    {
        latency = 0; // 0等待周期，适用于<=30MHz
    }
    else if (hclk_freq <= 60000000U)
    {
        latency = 1; // 1等待周期，适用于<=60MHz
    }
    else if (hclk_freq <= 90000000U)
    {
        latency = 2; // 2等待周期，适用于<=90MHz
    }
    else if (hclk_freq <= 120000000U)
    {
        latency = 3; // 3等待周期，适用于<=120MHz
    }
    else if (hclk_freq <= 150000000U)
    {
        latency = 4; // 4等待周期，适用于<=150MHz
    }
    else
    {
        latency = 5; // 5等待周期，适用于<=180MHz
    }
    
    return latency;
}

uint8_t FLASH_Init(void)
{
    uint32_t latency = FLASH_CalculateLatency();
    uint32_t temp_acr = FLASH_ACR;
    
    // 设置FLASH等待周期（清除原有LATENCY位，然后设置新值）
    temp_acr &= ~(0x0F); // 清除LATENCY位（低4位）
    temp_acr |= latency; // 设置新的等待周期值
    FLASH_ACR = temp_acr;
    
    // 检查等待周期是否设置成功
    if ((FLASH_ACR & 0x0F) != latency)
    {
        return FLASH_ERROR;
    }
    
    // 清除所有错误标志
    FLASH_ClearAllErrorFlags();
    
    return FLASH_OK;
}

// 以下函数暂时不需要，已移除以避免未使用函数警告
/*
static uint8_t FLASH_GetVoltageRange(void)
{
    uint8_t voltage_range = 0;
    
    // 根据实际情况确定电压范围，这里假设VDD为2.7V-3.6V（范围3）
    voltage_range = 3;
    
    return voltage_range;
}
*/

// FLASH解锁函数
static uint8_t FLASH_Unlock(void)
{
    // 检查FLASH是否已经解锁
    if ((FLASH_CR & CUSTOM_FLASH_CR_LOCK) != CUSTOM_FLASH_CR_LOCK)
    {
        return FLASH_OK; // 已经解锁
    }
    
    // 写入解锁密钥
    FLASH_KEYR = FLASH_KEY1;
    FLASH_KEYR = FLASH_KEY2;
    
    // 检查是否解锁成功
    if ((FLASH_CR & CUSTOM_FLASH_CR_LOCK) != CUSTOM_FLASH_CR_LOCK)
    {
        return FLASH_OK;
    }
    else
    {
        return FLASH_ERROR;
    }
}

// FLASH锁定函数
static void FLASH_Lock(void)
{
    FLASH_CR |= CUSTOM_FLASH_CR_LOCK;
}

uint8_t FLASH_EraseSector(uint32_t sector)
{
    uint32_t timeout = FLASH_TIMEOUT_VALUE;
    
    // 检查扇区是否有效
    if (sector > FLASH_SECTOR_7)
    {
        return FLASH_ERROR;
    }
    
    // 解锁FLASH
    if (FLASH_Unlock() != FLASH_OK)
    {
        return FLASH_ERROR;
    }
    
    // 清除所有错误标志
    FLASH_ClearAllErrorFlags();
    
    // 等待FLASH空闲
    timeout = FLASH_TIMEOUT_VALUE;
    while ((FLASH_SR & CUSTOM_FLASH_SR_BSY) && (timeout > 0))
    {
        timeout--;
    }
    
    if (timeout == 0)
    {
        FLASH_Lock();
        return FLASH_TIMEOUT;
    }
    
    // 选择要擦除的扇区并设置擦除位
    FLASH_CR &= ~CUSTOM_FLASH_CR_SNB_MASK; // 清除之前的扇区号
    FLASH_CR |= (sector << 3);      // 设置新的扇区号
    FLASH_CR |= CUSTOM_FLASH_CR_PER;       // 使能扇区擦除
    FLASH_CR |= CUSTOM_FLASH_CR_STRT;      // 开始擦除
    
    // 等待擦除完成
    timeout = FLASH_TIMEOUT_VALUE;
    while ((FLASH_SR & CUSTOM_FLASH_SR_BSY) && (timeout > 0))
    {
        timeout--;
    }
    
    if (timeout == 0)
    {
        FLASH_CR &= ~CUSTOM_FLASH_CR_PER; // 禁用扇区擦除
        FLASH_Lock();
        return FLASH_TIMEOUT;
    }
    
    // 禁用扇区擦除
    FLASH_CR &= ~CUSTOM_FLASH_CR_PER;
    
    // 检查是否有错误发生
    if (FLASH_SR & CUSTOM_FLASH_ALL_ERRORS)
    {
        // 清除错误标志
        FLASH_ClearAllErrorFlags();
        
        // 锁定FLASH防止意外写入
        FLASH_Lock();
        return FLASH_ERROR;
    }
    
    // 擦除完成后锁定FLASH
    FLASH_Lock();
    
    return FLASH_OK;
}

uint8_t FLASH_Write(uint32_t address, uint8_t *data, uint32_t length)
{
    uint32_t timeout = FLASH_TIMEOUT_VALUE;
    uint32_t remaining_bytes = length;
    uint32_t aligned_address = address;
    
    // 参数检查
    if ((data == NULL) || (length == 0))
    {
        return FLASH_ERROR;
    }
    
    // 检查是否在单个扇区内
    if (FLASH_CheckSingleSector(address, length) != FLASH_OK)
    {
        return FLASH_LENGTH_ERROR;
    }
    
    // 解锁FLASH
    if (FLASH_Unlock() != FLASH_OK)
    {
        return FLASH_ERROR;
    }
    
    // 清除所有错误标志
    FLASH_ClearAllErrorFlags();
    
    // 优先使用32位写入提高效率，处理对齐部分
    while (remaining_bytes >= 4 && (aligned_address & 0x03) == 0)
    {
        // 等待FLASH空闲
        timeout = FLASH_TIMEOUT_VALUE;
        while ((FLASH_SR & CUSTOM_FLASH_SR_BSY) && (timeout > 0))
        {
            timeout--;
        }
        
        if (timeout == 0)
        {
            FLASH_Lock();
            return FLASH_TIMEOUT;
        }
        
        // 使能编程
        FLASH_CR |= CUSTOM_FLASH_CR_PG;
        
        // 执行32位写入（需要按字节写入，因为STM32F4的FLASH接口需要这样操作）
        *(volatile uint8_t*)aligned_address = *((uint8_t*)(data + (aligned_address - address)));
        *(volatile uint8_t*)(aligned_address + 1) = *((uint8_t*)(data + (aligned_address - address) + 1));
        *(volatile uint8_t*)(aligned_address + 2) = *((uint8_t*)(data + (aligned_address - address) + 2));
        *(volatile uint8_t*)(aligned_address + 3) = *((uint8_t*)(data + (aligned_address - address) + 3));
        
        // 等待写入完成
        timeout = FLASH_TIMEOUT_VALUE;
        while ((FLASH_SR & CUSTOM_FLASH_SR_BSY) && (timeout > 0))
        {
            timeout--;
        }
        
        if (timeout == 0)
        {
            FLASH_CR &= ~CUSTOM_FLASH_CR_PG; // 禁用编程
            FLASH_Lock();
            return FLASH_TIMEOUT;
        }
        
        // 禁用编程
        FLASH_CR &= ~CUSTOM_FLASH_CR_PG;
        
        // 检查写入是否成功
        if (*(volatile uint32_t*)aligned_address != 
            *((uint32_t*)(data + (aligned_address - address))))
        {
            FLASH_Lock();
            return FLASH_ERROR;
        }
        
        aligned_address += 4;
        remaining_bytes -= 4;
    }
    
    // 处理剩余的字节（使用8位写入）
    while (remaining_bytes > 0)
    {
        // 等待FLASH空闲
        timeout = FLASH_TIMEOUT_VALUE;
        while ((FLASH_SR & CUSTOM_FLASH_SR_BSY) && (timeout > 0))
        {
            timeout--;
        }
        
        if (timeout == 0)
        {
            FLASH_Lock();
            return FLASH_TIMEOUT;
        }
        
        // 使能编程
        FLASH_CR |= CUSTOM_FLASH_CR_PG;
        
        // 执行8位写入
        *(volatile uint8_t*)aligned_address = *(data + (aligned_address - address));
        
        // 等待写入完成
        timeout = FLASH_TIMEOUT_VALUE;
        while ((FLASH_SR & CUSTOM_FLASH_SR_BSY) && (timeout > 0))
        {
            timeout--;
        }
        
        if (timeout == 0)
        {
            FLASH_CR &= ~CUSTOM_FLASH_CR_PG; // 禁用编程
            FLASH_Lock();
            return FLASH_TIMEOUT;
        }
        
        // 禁用编程
        FLASH_CR &= ~CUSTOM_FLASH_CR_PG;
        
        // 检查写入是否成功
        if (*(volatile uint8_t*)aligned_address != 
            *(data + (aligned_address - address)))
        {
            FLASH_Lock();
            return FLASH_ERROR;
        }
        
        aligned_address++;
        remaining_bytes--;
    }
    
    // 检查是否有错误发生
    if (FLASH_SR & CUSTOM_FLASH_ALL_ERRORS)
    {
        // 清除错误标志
        FLASH_ClearAllErrorFlags();
        
        // 锁定FLASH防止意外写入
        FLASH_Lock();
        return FLASH_ERROR;
    }
    
    // 写入完成后锁定FLASH
    FLASH_Lock();
    
    return FLASH_OK;
}

/**
 * @brief 清除FLASH所有错误标志
 * @note 使用此函数可以统一清除所有可能的FLASH错误标志
 * @return 无
 */
void FLASH_ClearAllErrorFlags(void)
{
    FLASH_SR |= CUSTOM_FLASH_ALL_ERRORS;
}

/**
 * @brief 读取FLASH
 * @param address 起始地址
 * @param buffer 存储读取数据的缓冲区
 * @param length 要读取的数据长度
 * @return FLASH_OK、FLASH_ADDR_ERROR或FLASH_LENGTH_ERROR
 */
uint8_t FLASH_Read(uint32_t address, uint8_t *buffer, uint32_t length)
{
    uint32_t i = 0;
    
    // 参数检查
    if ((buffer == NULL) || (length == 0))
    {
        return FLASH_ERROR;
    }
    
    // 检查地址有效性
    if (FLASH_CheckAddressValid(address, length) != FLASH_OK)
    {
        return FLASH_ADDR_ERROR;
    }
    
    // 使用更高效的方式读取数据（优先使用32位读取）
    uint32_t aligned_address = address;
    uint32_t remaining_bytes = length;
    
    // 处理未对齐的起始部分
    while (remaining_bytes > 0 && (aligned_address & 0x03) != 0)
    {
        buffer[i] = *(volatile uint8_t*)aligned_address;
        aligned_address++;
        i++;
        remaining_bytes--;
    }
    
    // 使用32位读取提高效率
    while (remaining_bytes >= 4)
    {
        uint32_t word_value = *(volatile uint32_t*)aligned_address;
        
        // 将32位数据分解到缓冲区
        buffer[i] = (uint8_t)(word_value & 0xFF);
        buffer[i + 1] = (uint8_t)((word_value >> 8) & 0xFF);
        buffer[i + 2] = (uint8_t)((word_value >> 16) & 0xFF);
        buffer[i + 3] = (uint8_t)((word_value >> 24) & 0xFF);
        
        aligned_address += 4;
        i += 4;
        remaining_bytes -= 4;
    }
    
    // 处理剩余的字节
    while (remaining_bytes > 0)
    {
        buffer[i] = *(volatile uint8_t*)aligned_address;
        aligned_address++;
        i++;
        remaining_bytes--;
    }
    
    return FLASH_OK;
}